RESTORE_ALL:
    ;按照顺序一次从栈中恢复保存的寄存器值，恢复时当前栈指针RSP指向保存在栈中的r15的值
    popq %r15;
    popq %r14;
    popq %r13;
    popq %r12;
    popq %r11;
    popq %r10;
    popq %r9;
    popq %r8;
    popq %rbx;
    popq %rcx;
    popq %rsi;
    popq %rdi;
    popq %rbp;
    ;popq %ds是无效操作，因此需要先将栈中的信息缓存在rax中，再将rax的数据拷贝到ds
    popq %rax;
    movq %rax, %ds;
    popq %rax;
    movq %rax, %es;
    ;最后恢复rax
    popq %rax;

    ;弹出栈中的变量FUNC和ERRCODE，之后便可以执行汇编代码iretq，还原被中断程序的执行现场
    addq $0x10, %rsp;
    ;该指令会自行判断是否涉及特权级切换，如果是就将OLDSS、OLDRSP从栈中弹出，使用这个指令，我们少了一次判断
    iretq;

ENTRY(divide_error)
    ;#DE异常不会产生错误码，压入0进行占位
    ;首先保存的是异常处理函数地址和错误码
    pushq $0
    pushq %rax
    ;将do_divide_error的地址存入寄存器rax中
    leaq do_divide_error(%rip), %rax
    ;借助xchgq将寄存器rax和栈中的值交换，这样rax能够恢复原来的值，栈中也成功保存了函数地址
    xchgq %rax, (%rsp)

error_code:
    ;按照顺序保存寄存器
    pushq %rax
    movq %es, %rax
    pushq %rax
    movq %ds, %rax
    pushq %rax
    ;将rax寄存器清0
    xorq %rax

    pushq %rbp
    pushq %rdi
    pushq %rsi
    pushq %rdx
    pushq %rcx
    pushq %rbx
    pushq %r8
    pushq %r9
    pushq %r10
    pushq %r11
    pushq %r12
    pushq %r13
    pushq %r14
    pushq %r15

    ;进程的执行现场保存完毕后，就可以执行对应的异常处理函数
    cld
    ;清除方向标志位，这样rdi就能在movq时自动递增
    ;又有被中断的程序可能运行在应用层，而异常处理程序运行在内核层，那么在进入内核层后，DS和ES段寄存器应该重新加载为内核层数据段
    ;错误码存入rsi
    ;ERRCODE是什么，为什么是rsp
    movq ERRCODE(%rsp), %rsi
    ;异常处理函数的起始地址装入rdx寄存器
    movq FUNC(%rsp), %rdx

    ;即将进入内核层，DS和ES段寄存器应该重新加载为内核层数据段
    movq $0x10, %rdi
    ;0x10
    movq %rdi, %ds
    ;0x18
    movq %rdi, %es
    ;栈指针存入rdi
    movq %rsp, %rdi

    ;执行异常处理函数
    callq *%rdx
    jmp ret_from_exception

ret_from_exception:
    /*GET_CURRENT(%ebx) need rewrite*/
ENTRY(ret_from_intr)
    jmp RESTORE_ALL

ENTRY(nmi)
    ;占位
    pushq %rax
    cld
    ;占位
    pushq %rax

    ;按照顺序保存寄存器
    pushq %rax
    movq %es, %rax
    pushq %rax
    movq %ds, %rax
    pushq %rax
    ;将rax寄存器清0
    xorq %rax

    pushq %rbp
    pushq %rdi
    pushq %rsi
    pushq %rdx
    pushq %rcx
    pushq %rbx
    pushq %r8
    pushq %r9
    pushq %r10
    pushq %r11
    pushq %r12
    pushq %r13
    pushq %r14
    pushq %r15

    movq $0x10, %rdi
    ;0x10
    movq %rdi, %ds
    ;0x18
    movq %rdi, %es
    ;栈指针存入rdi

    movq $0, %rsi
    movq %rsp, %rdi
    call do_nmi

    jmp
ENTRY(invalid_TSS)
    pushq %rax
    leaq do_invalid_TSS(%rip), %rax
    xchgq %rax, (%rsp)
    jum error_code
ENTRY(page_fault)
    pushq %rax
    leaq do_page_fault(%rip), %rax
    xchgq %rax, (%rsp)
    jmp error_code
